#pragma once

#include "sock.hpp"
#include <sys/select.h>
#include <string>

#define BACK_LOG 5
#define NUM 5
#define DFL_FD -1

namespace  ns_select
{
  
  class SelectServer
  {
  public:
    SelectServer(unsigned short port)
      :_port(port)
    {}
    bool InitServer()
    {
      // 创建套接字
      _lsock = ns_sock::Sock::CreateSocket();
      // 绑定
      ns_sock::Sock::Bind(_lsock, _port);
      // 监听
      ns_sock::Sock::Listen(_lsock, BACK_LOG);
    }
    void Run()
    {
      fd_set rfds;// 读位图
      int fd_array[NUM] = {0};
      InitArray(fd_array, NUM, DFL_FD);
      // 将监听套接字添加到标记数组中
      fd_array[0] = _lsock;
      while (1)
      {
        int maxfd = _lsock;
        struct timeval timeout = {5, 0};
        FD_ZERO(&rfds);
        for (int i = 0; i < NUM; ++i)
        {
          if (fd_array[i] == DFL_FD) continue;
          FD_SET(fd_array[i], &rfds);// 把要关心的fd添加到位图中
          if (fd_array[i] > maxfd) maxfd = fd_array[i];
        }
        int ret = select(maxfd+1, &rfds, nullptr, nullptr, &timeout);
        switch(ret)
        {
          case 0:
            std::cout << "timeout:" << timeout.tv_sec << std::endl;
            break;
          case -1:
            std::cerr << "select error, errno is " << errno << std::endl;
            break;
          default:
            //std::cout << "to do something, timeout is " << timeout.tv_sec << std::endl;
            std::cout << "changed fd num is " << ret << std::endl;
            Print(fd_array, NUM, &rfds);
            HandEvent(fd_array, NUM, &rfds);
            break;
        }
      }
    }
    ~SelectServer()
    {
      if (_lsock >= 0)
        close(_lsock);
    }
  private:
    void HandEvent(int fd_array[], int size, fd_set* rfds)
    {
      for (int i = 0; i < size; ++i)
      {
        if (FD_ISSET(fd_array[i], rfds)){
          if (fd_array[i] == _lsock){
            // 连接就绪
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sock = accept(_lsock, (struct sockaddr*)&peer, &len);
            
            std::string peerIp = inet_ntoa(peer.sin_addr);
            unsigned short peerPort = ntohs(peer.sin_port);
            
            std::cout << "get a new link:[" << peerIp << ":" << peerPort << "]" << std::endl; 
            // 把套接字添加到fd_array中，让select等待读就绪
            AddFdToArray(fd_array, NUM, sock);
          }
          else{
            // 读就绪
            char buf[1024];
            ssize_t sz = recv(fd_array[i], buf, sizeof(buf)-1, 0);
            if (sz > 0){
              buf[sz] = 0;
              std::cout << "echo# " << buf << std::endl;
            }
            else if (sz == 0){
              // 对端关闭
              std::cout << "client close" << std::endl;
              close(fd_array[i]);
              fd_array[i] = DFL_FD;
            }
            else{
              std::cerr << "recv error" << std::endl;
              close(fd_array[i]);
              fd_array[i] = DFL_FD;
            }
          }
        }
      }  
    }
    void Print(int fd_array[], int size, fd_set* rfds)
    {
      for (int i = 0; i < size; ++i)
      {
        if (FD_ISSET(fd_array[i], rfds)){
          std::cout << "1";
        }
        else{
          std::cout << "0";
        }
      }
      std::cout << std::endl;
    }
    bool AddFdToArray(int fd_array[], int size, int sock)
    {
      for (int i = 0; i < size; ++i)
      {
        if (fd_array[i] == DFL_FD){
          fd_array[i] = sock;
          return true;
        }
      }
      return false;
    }
    void InitArray(int fd_array[], int size, int flag)
    {
      for (int i = 0; i < size; ++i)
      {
        fd_array[i] = flag;
      }
    }
  private:
    unsigned short _port;
    int _lsock;
  };
}
